<template>
    <div ref="container" class="model-container">
      <div v-if="loading" class="loading-overlay">
        <div class="loading-content">
          <div class="spinner"></div>
          <div>加载中... {{ progress }}%</div>
        </div>
      </div>
      <div v-if="error" class="error-overlay">
        <div class="error-content">
          <div class="error-icon">⚠️</div>
          <div>加载失败: {{ error }}</div>
          <button @click="loadModel">重试</button>
        </div>
      </div>
    </div>
  </template>
  
  <script setup>
  import { ref, watch, onMounted, onBeforeUnmount } from 'vue';
  import * as THREE from 'three';
  import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
  import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js';
  import { OBJLoader } from 'three/addons/loaders/OBJLoader.js';
  import { FBXLoader } from 'three/addons/loaders/FBXLoader.js';
  import { STLLoader } from 'three/addons/loaders/STLLoader.js';
  import { DRACOLoader } from 'three/addons/loaders/DRACOLoader.js';
  import { ThreeMFLoader } from 'three/addons/loaders/3MFLoader.js';
  
  const props = defineProps({
    modelUrl: {
      type: String,
      required: true
    },
    modelType: {
      type: String,
      required: true,
      validator: (value) => ['gltf', 'glb', 'obj', 'fbx', 'stl', '3mf'].includes(value)
    },
    backgroundColor: {
      type: String,
      default: '#f0f0f0'
    },
    showAxes: {
      type: Boolean,
      default: true
    },
    dracoDecoderPath: {
      type: String,
      default: 'https://www.gstatic.com/draco/versioned/decoders/1.5.6/'
    },
    modelColor: {
      type: String,
      default: '#aaaaaa'
    }
  });
  
  const emit = defineEmits(['loading', 'loaded', 'error', 'progress']);
  
  const container = ref(null);
  const loading = ref(false);
  const progress = ref(0);
  const error = ref(null);
  
  let scene, camera, renderer, controls, currentModel, modelGroup;
  
  // 初始化场景
  function initScene() {
    // 创建场景
    scene = new THREE.Scene();
    scene.background = new THREE.Color(props.backgroundColor);
    
    // 创建相机
    camera = new THREE.PerspectiveCamera(
      75,
      container.value.clientWidth / container.value.clientHeight,
      0.1,
      1000
    );
    camera.position.set(0, 0, 5);
    
    // 创建渲染器
    renderer = new THREE.WebGLRenderer({ antialias: true });
    renderer.setSize(container.value.clientWidth, container.value.clientHeight);
    container.value.appendChild(renderer.domElement);
    
    // 添加轨道控制器
    controls = new OrbitControls(camera, renderer.domElement);
    controls.enableDamping = true;
    controls.dampingFactor = 0.05;
    
    // 添加光源
    const ambientLight = new THREE.AmbientLight(0xffffff, 0.5);
    scene.add(ambientLight);
    
    const directionalLight = new THREE.DirectionalLight(0xffffff, 1);
    directionalLight.position.set(5, 10, 7);
    scene.add(directionalLight);
  
    // 添加坐标轴辅助
    if (props.showAxes) {
      const axesHelper = new THREE.AxesHelper(5);
      scene.add(axesHelper);
    }
  
    // 创建模型组
    modelGroup = new THREE.Group();
    scene.add(modelGroup);
  }
  
  // 从URL加载模型
  async function loadModel() {
    if (!props.modelUrl) return;
    
    loading.value = true;
    error.value = null;
    emit('loading', true);
    
    try {
      // 清除旧模型
      if (currentModel) {
        modelGroup.remove(currentModel);
        currentModel = null;
      }
      
      // 根据不同类型使用不同的加载方式
      switch (props.modelType) {
        case 'gltf':
        case 'glb':
          await loadGLTF(props.modelUrl);
          break;
        case 'obj':
          await loadOBJ(props.modelUrl);
          break;
        case 'fbx':
          await loadFBX(props.modelUrl);
          break;
        case 'stl':
          await loadSTL(props.modelUrl);
          break;
        case '3mf':
          await load3MF(props.modelUrl);
          break;
        default:
          throw new Error('不支持的模型格式');
      }
      
      emit('loaded');
    } catch (err) {
      error.value = err.message || '加载模型失败';
      emit('error', err);
    } finally {
      loading.value = false;
      emit('loading', false);
    }
  }
  
  // 加载GLTF/GLB模型
  async function loadGLTF(url) {
    const loader = new GLTFLoader();
    
    // 设置DRACO解码器
    const dracoLoader = new DRACOLoader();
    dracoLoader.setDecoderPath(props.dracoDecoderPath);
    loader.setDRACOLoader(dracoLoader);
    
    return new Promise((resolve, reject) => {
      loader.load(
        url,
        (gltf) => {
          currentModel = gltf.scene;
          prepareModel(currentModel);
          resolve();
        },
        (xhr) => {
          const percent = (xhr.loaded / xhr.total * 100).toFixed(2);
          progress.value = parseFloat(percent);
          emit('progress', progress.value);
        },
        (error) => {
          reject(error);
        }
      );
    });
  }
  
  // 加载OBJ模型
  async function loadOBJ(url) {
    const loader = new OBJLoader();
    
    return new Promise((resolve, reject) => {
      loader.load(
        url,
        (object) => {
          currentModel = object;
          prepareModel(currentModel);
          resolve();
        },
        (xhr) => {
          const percent = (xhr.loaded / xhr.total * 100).toFixed(2);
          progress.value = parseFloat(percent);
          emit('progress', progress.value);
        },
        (error) => {
          reject(error);
        }
      );
    });
  }
  
  // 加载FBX模型
  async function loadFBX(url) {
    const loader = new FBXLoader();
    
    return new Promise((resolve, reject) => {
      loader.load(
        url,
        (object) => {
          currentModel = object;
          prepareModel(currentModel);
          resolve();
        },
        (xhr) => {
          const percent = (xhr.loaded / xhr.total * 100).toFixed(2);
          progress.value = parseFloat(percent);
          emit('progress', progress.value);
        },
        (error) => {
          reject(error);
        }
      );
    });
  }
  
  // 加载STL模型
  async function loadSTL(url) {
    const loader = new STLLoader();
    
    return new Promise((resolve, reject) => {
      loader.load(
        url,
        (geometry) => {
          const material = new THREE.MeshStandardMaterial({
            color: new THREE.Color(props.modelColor),
            metalness: 0.5,
            roughness: 0.5,
          });
          
          currentModel = new THREE.Mesh(geometry, material);
          prepareModel(currentModel);
          resolve();
        },
        (xhr) => {
          const percent = (xhr.loaded / xhr.total * 100).toFixed(2);
          progress.value = parseFloat(percent);
          emit('progress', progress.value);
        },
        (error) => {
          reject(error);
        }
      );
    });
  }
  
  // 加载3MF模型
  async function load3MF(url) {
    const loader = new ThreeMFLoader();
    
    return new Promise((resolve, reject) => {
      loader.load(
        url,
        (object) => {
          currentModel = object;
          prepareModel(currentModel);
          resolve();
        },
        (xhr) => {
          const percent = (xhr.loaded / xhr.total * 100).toFixed(2);
          progress.value = parseFloat(percent);
          emit('progress', progress.value);
        },
        (error) => {
          reject(error);
        }
      );
    });
  }
  
  // 准备模型（居中、缩放并添加到场景）
  function prepareModel(model) {
    // 计算模型包围盒
    const box = new THREE.Box3().setFromObject(model);
    const center = box.getCenter(new THREE.Vector3());
    const size = box.getSize(new THREE.Vector3());
    
    // 将模型位置调整为相对于组中心
    model.position.x -= center.x;
    model.position.y -= center.y;
    model.position.z -= center.z;
    
    // 计算合适的缩放比例
    const maxDim = Math.max(size.x, size.y, size.z);
    const targetSize = 5; // 目标尺寸
    const scale = targetSize / maxDim;
    model.scale.set(scale, scale, scale);
    
    // 将模型添加到组中
    modelGroup.add(model);
    
    // 重置控制器目标
    controls.target.set(0, 0, 0);
    
    // 调整相机位置
    camera.position.set(0, 0, maxDim * 1.5 * scale);
    controls.update();
  }
  
  // 处理窗口大小变化
  function onWindowResize() {
    camera.aspect = container.value.clientWidth / container.value.clientHeight;
    camera.updateProjectionMatrix();
    renderer.setSize(container.value.clientWidth, container.value.clientHeight);
  }
  
  // 动画循环
  function animate() {
    requestAnimationFrame(animate);
    controls.update();
    renderer.render(scene, camera);
  }
  
  // 监听props变化
  watch(() => [props.modelUrl, props.modelType], () => {
    loadModel();
  });
  
  onMounted(() => {
    initScene();
    animate();
    window.addEventListener('resize', onWindowResize);
    loadModel();
  });
  
  onBeforeUnmount(() => {
    window.removeEventListener('resize', onWindowResize);
    if (renderer) {
      renderer.dispose();
    }
  });
  </script>
  
  <style scoped>
  .model-container {
    width: 100%;
    height: 100%;
    min-height: 500px;
    position: relative;
  }
  
  .loading-overlay, .error-overlay {
    position: absolute;
    top: 0;
    left: 0;
    right: 0;
    bottom: 0;
    display: flex;
    justify-content: center;
    align-items: center;
    background-color: rgba(0, 0, 0, 0.5);
    z-index: 100;
  }
  
  .loading-content, .error-content {
    background: white;
    padding: 20px;
    border-radius: 8px;
    text-align: center;
  }
  
  .spinner {
    border: 4px solid rgba(0, 0, 0, 0.1);
    width: 36px;
    height: 36px;
    border-radius: 50%;
    border-left-color: #09f;
    animation: spin 1s linear infinite;
    margin: 0 auto 10px;
  }
  
  .error-icon {
    font-size: 36px;
    margin-bottom: 10px;
  }
  
  @keyframes spin {
    0% { transform: rotate(0deg); }
    100% { transform: rotate(360deg); }
  }
  
  button {
    margin-top: 10px;
    padding: 5px 15px;
    background: #09f;
    color: white;
    border: none;
    border-radius: 4px;
    cursor: pointer;
  }
  </style>